/**
 * Combo box control.
 *
 * @author Andrey Anisimov <hercules@inbox.ru>
 * @see core.ui.ListBox
 */

import core.ui.CompositeWindow;
import core.ui.ImageButton;
import core.ui.Edit;
import core.ui.Event;
import core.ui.ComboBoxMenu;
import core.util.Listeners;

class ComboBox extends CompositeWindow {
	
	/**
	 * Creates new combo box.
	 * 
	 * @param parent (Window) [optional] parent window
	 * @param x, y (int) [optional] coordinates
	 * @param width, height (int) [optional] combo box size.
	 */
	this.ComboBox = function(parent, x, y, width, height) {
		super(parent, x, y, width, height);

		this.listeners = new Listeners(['onValueChanged']);

		this.text = new Edit(this, 0, 0);
		this.text.setStyle('float', 'left');
		this.text.onkeypress = bind(this, this.onTextKeyPress);
		
		this.dropDownButton = new ImageButton(this);
		this.dropDownButton.setStyleName('DropDownButton');
		this.dropDownButton.setState(ImageButton.STATE_NORMAL);
		this.dropDownButton.setStyle('float', 'right');
		this.dropDownButton.oncommand = bind(this, this.onDropDownButtonClick);
		
		this.menu = this.createMenu();
		this.menu.addMenuListener(this);
		
		// By default ComboBox is not editable.
		this.setEditable(false);
		this.updateLayout();
	}

	this.addChangeListener = function(listener) {
		this.listeners.add(listener);
	}

	this.removeChangeListener = function(listener) {
		this.listeners.remove(listener);
	}

	/**
	 * Adds new item to the ComboBox menu.
	 * 
	 * @param text (string) item text
	 * @param value (mixed) item value
	 * @param selected (bool) whether the new item selected
	 */
	this.addItem = function(text, value, selected) {
		var item = this.menu.addItem(text, value);
		if (selected) {
			this.setText(text);
		}
		return item;
	}
	
	/**
	 * Inserts new item to combo box (unimplemented).
	 * 
	 * @param i (int) new item position
	 * @param text (string) new item label
	 * @param selected (bool) whether the item is selected.
	 */
	this.insertItem = function(i, text, selected) {
		// TODO: implement
	}
	
	/**
	 * Removes item from combo box (unimplemented).
	 * 
	 * @param i (int) item position.
	 */
	this.removeItem = function(i) {
		this.menu.removeItem(i);
	}
	
	/**
	 * Removes all items from combo box.
	 */
	this.removeItems = function() {
		this.menu.removeItems();
	}
	
	/**
	 * Sets the text to display in combo box editable area.
	 * 
	 * @param text (string) text to display.
	 */
	this.setText = function(text) {
		this.text.setText(text);
	}
	
	/**
	 * Returns text currently displayed in combo box.
	 * 
	 * @return string
	 */
	this.getText = function() {
		return this.text.getText();
	}
	
	/**
	 * Sets current combo box value.
	 *
	 * Note:
	 * Calling this method you will change current combo box value
	 * and text displayed in combo box editable area. If you want
	 * to change text only, use setText() instead.
	 * 
	 * @param value (variant) new value
	 */
	this.setValue = function(value) {
		if (this.value == value) return;
		this.value = value;
		for (var i = 0; i < this.menu.getItemCount(); i++) {
			var item = this.menu.getItem(i);
			if (value == item.value) {
				this.menu.setSelectedItem(item);
				this.setText(item.getText());
				this.scrollTo(i);
			}
		}
	}
	
	/**
	 * Returns current combo box value.
	 * 
	 * @return variant
	 */
	this.getValue = function() {
		return this.value;
	}
	
	/**
	 * Returns combo box menu control.
	 * 
	 * @return Menu
	 */
	this.getMenu = function() {
		return this.menu;
	}
	
	/**
	 * Returns child button control.
	 * 
	 * @return ImageButton
	 */
	this.getDropDownButton = function() {
		return this.dropDownButton;
	}
	
	/**
	 * Returns child edit control.
	 * 
	 * @return Edit
	 */
	this.getEditControl = function() {
		return this.text;
	}
	
	/**
	 * Sets the combo box editable or not.
	 *
	 * Note: the ComboBox can be both selectable and editable.
	 * Setting this method to false causes the edit area of ComboBox
	 * becomes read only and user cannot edit the text.
	 * 
	 * @param editable (bool) true if ComboBox should be editable, false otherwise.
	 */
	this.setEditable = function(editable) {
		this.text.setReadOnly(!editable);
		this.text.setStyle('cursor', (editable) ? 'text' : 'default');
		this.text.onclick = (editable) ? null : bind(this, this.onDropDownButtonClick);
		this.text.onkeypress = (editable) ? bind(this, this.onTextKeyPress) : null;
	}
	
	/**
	 * Returns whether the ComboBox is editable for users.
	 * 
	 * @return bool
	 */
	this.isEditable = function() {
		return !this.text.isReadOnly();
	}

	/**
	 * Scrolls combobox list to specified item.
	 * 
	 * @param i (int) item index
	 */
	this.scrollTo = function(i) {
		this.menu.ensureVisible(i);
	}
	
	/**
	 * Closes combobox and its popup.
	 */
	this.close = function() {
		this.menu.close();
		super.close();
	};
	
	/**
	 * Expands combobox menu.
	 */
	this.expand = function() {
		this.addStyleName(this.getStyleName() + '-expanded');
		this.text.setSelected(true);
		this.menu.doPopup(this.isEditable() ? this.dropDownButton : this,
			this.getAbsX(),
			this.getAbsY() + this.getHeight());
	}
	
	/**
	 * Collapses combobox menu.
	 */
	this.collapse = function() {
		this.menu.endPopup();
	}
	
	/**
	 * @protected
	 * Creates and returns a menu for this combo box.
	 * 
	 * @return ComboBoxMenu
	 */
	this.createMenu = function() {
		return new ComboBoxMenu();
	}
	
	/**
 	 * @protected
	 * Calls automatically every time window size is changed.
	 */
	this.updateLayout = function() {
		this.text.setStyle('width', (this.getClientWidth() - this.dropDownButton.getWidth()) + 'px');
		this.menu.setStyle('width', this.getWidth() + 'px');
	}

	/**
	 * Calls when combo box valie is changed.
	 */
	this.fireChanged = function() {
		this.listeners.call('onValueChanged', [this]);
	}

	this.onDropDownButtonClick = function(event) {
		if (this.menu.isPopup()) {
			this.collapse();
		} else {
			this.expand();
		}
	}
	
	this.onEndPopup = function() {
		this.removeStyleName(this.getStyleName() + '-expanded');
	}
	
	this.onItemSelected = function(menu) {
		var item = menu.getSelectedItem();
		this.menu.endPopup();
		this.setText(item.getText());
		this.setValue(item.value);
		this.text.setSelected(false);
		this.fireChanged();
	}
	
	this.onTextKeyPress = function(event) {
        switch (event.keyCode) {
            case Event.KEY_ENTER:
                this.value = this.text.getText();
                this.fireChanged();
                break;
        }
	}
}